// ===== 极简版前端文字转语音 =====
// 仅调用后端统一接口 POST /api/v1/tts (MD 规范)；后端再负责与火山引擎交互。
// 返回格式：{ reqid, code, operation, message, sequence, data(base64), addition? }
// 功能：
// 1. 顺序播放队列
// 2. 文本+音色缓存（内存，仅当前会话）
// 3. 无音频 / 错误回退浏览器 speechSynthesis
// 4. 支持流式TTS播放

import config from '@/config.js';
import streamingTtsService from './streamingTts.js';
import streamingTtsProxy from './streamingTtsProxy.js';

const TTS_BASE = (config?.wsBaseUrl || config?.baseUrl || '').replace(/\/$/, '');
const API_URL = TTS_BASE + '/api/v1/tts';

const cache = new Map(); // key = voice|text -> objectURL
const queue = [];
let playing = false;
let autoWarmupDone = false; // 静音预热标记
let currentFallbackUtterance = null; // 当前 speechSynthesis 回退
const pendingSet = new Set(); // 未播放的文本去重
const lastPlayAt = new Map(); // 文本上次播放时间
const SAME_TEXT_INTERVAL = 2500; // ms 内重复文本忽略（可被 opts.allowRepeat 覆盖）
// --- 事件机制 ---
const ttsListeners = new Set();
function emitTts(evt){ ttsListeners.forEach(fn=>{ try{ fn(evt); }catch(_){} }); }
export function onTtsEvent(fn){ if(typeof fn==='function'){ ttsListeners.add(fn); return ()=> ttsListeners.delete(fn); } return ()=>{}; }
export function offTtsEvent(fn){ ttsListeners.delete(fn); }
// 无手势环境下尝试通过静音预热来获取部分浏览器的后续播放许可

function genReqId(){
  if(typeof crypto!=='undefined' && crypto.randomUUID) return crypto.randomUUID();
  return 'req-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2,8);
}
function b64ToUrl(b64){
  const bin = atob(b64); const len = bin.length; const u8 = new Uint8Array(len); for(let i=0;i<len;i++) u8[i]=bin.charCodeAt(i);
  return URL.createObjectURL(new Blob([u8], {type:'audio/mpeg'}));
}
function play(url){
  return new Promise((res,rej)=>{
    const a = new Audio();
    a.src = url;
    a.preload = 'auto';
    a.autoplay = true;
    a.playsInline = true;
    a.onended = res;
    a.onerror = ()=> rej(new Error('audio error'));
  // 一旦真实音频开始播放，取消任何正在进行的 speechSynthesis 回退，确保只有一个声音
  a.onplay = ()=> { try{ if(currentFallbackUtterance && window.speechSynthesis){ window.speechSynthesis.cancel(); currentFallbackUtterance = null; } }catch{} };
    let attempt = 0;
    const MAX_ATTEMPTS = 6;
    const tryPlay = ()=>{
      const p = a.play();
      if(!p || !p.catch){ return; }
      p.catch(err=>{
        const msg = (err && (err.message||err.name||'')).toLowerCase();
        if(/notallowed|autoplay|gesture|interact/.test(msg) && attempt < MAX_ATTEMPTS){
          attempt++;
          // 静音预热再尝试
          a.muted = true;
          a.play().then(()=>{
            if(process.env.NODE_ENV!=='production') console.log('[TTS] muted warmup ok, retry unmute');
            setTimeout(()=>{ a.muted=false; tryPlay(); }, 120);
          }).catch(()=>{
            // 使用一个隐藏静音占位音频尝试获取 autoplay (某些浏览器允许 muted autoplay)
            if(!autoWarmupDone){
              autoWarmupDone = true;
              try{ const dummy = new Audio('data:audio/mp3;base64,//uQxAAAAAAAAAAAAAAAAAAAA'); dummy.muted=true; dummy.play().catch(()=>{}); }catch{}
            }
            setTimeout(tryPlay, 600 + attempt*300);
          });
        } else if(/notallowed|autoplay|gesture|interact/.test(msg)){
          // 最终放弃：交给 fallback speechSynthesis（不会再抛出 AUTOPLAY_BLOCK 给上层以免阻断队列）
          if(process.env.NODE_ENV!=='production') console.warn('[TTS] autoplay permanently blocked after retries');
          res(); // 结束，外层会用 fallback
        } else {
          rej(err);
        }
      });
    };
    tryPlay();
  });
}
function speakFallback(text){
  if(!('speechSynthesis' in window)) return;
  try{window.speechSynthesis.cancel();}catch{}
  const u = new SpeechSynthesisUtterance(text);
  u.lang = 'zh-CN';
  currentFallbackUtterance = u;
  try{ window.speechSynthesis.speak(u); }catch{ currentFallbackUtterance = null; }
}

async function requestTTS(text, opts={}){
  // 传统TTS请求不需要前端配置敏感信息，由后端处理
  const voice = opts.voice || config?.tts?.defaultVoice || 'zh_female_kefunvsheng_mars_bigtts';
  const started = Date.now();
  
  const body = {
    user: { uid: 'remind_user' },
    audio: { 
      voice_type: voice, 
      encoding: 'mp3',
      sample_rate: config?.tts?.sampleRate || 16000
    },
    request: { reqid: genReqId(), operation: 'query', text }
  };
  
  if(process.env.NODE_ENV !== 'production') {
    console.log('[TTS] 请求配置:', { voice, textLength: text.length });
  }
  
  const resp = await fetch(API_URL, { 
    method: 'POST', 
    headers: { 'Content-Type': 'application/json' }, 
    body: JSON.stringify(body) 
  });
  
  if(!resp.ok){
    emitTts({ type:'http_error', status: resp.status, text, voice, reqid: body.request.reqid, cost: Date.now()-started });
    throw new Error('http '+resp.status);
  }
  
  const json = await resp.json();
  if(json.code !== 3000 || !json.data){
    emitTts({ type:'fail', code: json.code, message: json.message, text, voice, reqid: body.request.reqid, cost: Date.now()-started });
    throw new Error(json.message || 'TTS_FAIL');
  }
  
  const b64 = json.data;
  emitTts({ type:'success', code: json.code, message: json.message, text, voice, reqid: body.request.reqid, bytes: Math.floor(b64.length*3/4), cost: Date.now()-started });
  return { b64, voice };
}

async function playOne(text, opts){
  const voice = opts?.voice || config?.tts?.defaultVoice || 'default';
  const key = voice + '|' + text;
  // 再次防重：如果太频繁则跳过播放
  const now0 = Date.now();
  const lp = lastPlayAt.get(key);
  if(!opts?.allowRepeat && lp && now0 - lp < SAME_TEXT_INTERVAL){
    if(process.env.NODE_ENV!=='production') console.log('[TTS] skip duplicate within interval', text);
    return;
  }
  let gotAudio = false;
  let url;
  // 1. 取缓存或请求
  if(cache.has(key)){
    url = cache.get(key);
    gotAudio = true;
  } else {
    try {
      const { b64 } = await requestTTS(text, opts); // 若抛异常 -> 请求层失败，触发 fallback
      url = b64ToUrl(b64);
      cache.set(key, url);
      gotAudio = true;
    } catch(e){
      if(process.env.NODE_ENV!=='production') console.warn('[TTS] request error -> fallback', e.message||e);
      emitTts({ type:'fallback', reason:'request_error', text, voice });
      speakFallback(text);
      lastPlayAt.set(key, Date.now());
      await new Promise(r=> setTimeout(r, Math.min(6000, text.length*300)));
      return; // 结束，不再继续后续播放逻辑
    }
  }
  // 2. 播放真实音频
  if(gotAudio && url){
    try {
      await play(url);
    } catch(e){
      if(process.env.NODE_ENV!=='production') console.warn('[TTS] audio play error -> fallback', e.message||e);
      emitTts({ type:'fallback', reason:'play_error', text, voice });
      speakFallback(text);
      await new Promise(r=> setTimeout(r, Math.min(6000, text.length*300)));
    }
  }
  lastPlayAt.set(key, Date.now());
}

async function runQueue(){
  if(playing) return; playing = true;
  while(queue.length){
    const {text, opts, resolve} = queue.shift();
    pendingSet.delete(text);
    try { await playOne(text, opts); } catch {/* 已有内部回退 */}
    try { resolve(); } catch {}
  }
  // 清空 pendingSet
  pendingSet.clear();
  playing = false;
}

// （已移除解锁逻辑，无需额外 API）

export function ttsPlay(text, opts={}){
  if(!text) return Promise.resolve();
  text = String(text);
  // 若需要统一播报格式：只播放已加【】的格式化文本（含时间），未格式化直接忽略
  if(!/【.+】/.test(text)){
    if(process.env.NODE_ENV!=='production') console.log('[TTS] skip unformatted text', text);
    return Promise.resolve();
  }
  // 去掉手势 gating，仅基于 pendingSet 去重
  if(pendingSet.has(text)) return Promise.resolve();
  // 短时间重复播放过滤（即便已解锁）
  const voice = opts.voice || config?.tts?.defaultVoice || 'default';
  const key = voice + '|' + text;
  const now = Date.now();
  if(!opts.allowRepeat && lastPlayAt.get(key) && now - lastPlayAt.get(key) < SAME_TEXT_INTERVAL){
    if(process.env.NODE_ENV!=='production') console.log('[TTS] reject duplicate request', text);
    return Promise.resolve();
  }
  return new Promise(res=> {
    queue.push({ text, opts, resolve: res });
    pendingSet.add(text);
  emitTts({ type:'enqueue', text, voice, size: queue.length });
    // 如果之前被拦截，等待用户手势重新开始
    runQueue();
  });
}

/**
 * 流式TTS播放 - 使用WebSocket实时合成和播放
 */
export async function ttsPlayStreaming(text, opts = {}) {
  if (!text) return Promise.resolve();
  text = String(text);
  
  // 检查是否启用流式模式
  if (!opts.useStreaming && !config?.tts?.enableStreaming) {
    // 降级到传统TTS
    return ttsPlay(text, opts);
  }
  
  try {
    console.log('[TTS] 开始流式播放:', text);
    emitTts({ type: 'streaming_start', text, opts });
    
    // 优先使用代理模式
    if (opts.useProxy !== false) {
      return await playWithStreamingTtsProxy(text, opts);
    } else {
      return await playWithStreamingTtsDirect(text, opts);
    }
  } catch (error) {
    console.error('[TTS] 流式播放失败:', error);
    emitTts({ type: 'streaming_error', text, error: error.message });
    
    // 降级到传统TTS
    return ttsPlay(text, opts);
  }
}

/**
 * 使用后端代理的流式TTS播放
 */
async function playWithStreamingTtsProxy(text, opts = {}) {
  return new Promise((resolve, reject) => {
    streamingTtsProxy.on('completed', () => {
      console.log('[TTS] 代理流式播放完成');
      emitTts({ type: 'streaming_completed', text });
      resolve();
    });
    
    streamingTtsProxy.on('error', (error) => {
      console.error('[TTS] 代理流式播放错误:', error);
      emitTts({ type: 'streaming_error', text, error: error.message });
      reject(error);
    });
    
    // 连接并播放
    streamingTtsProxy.connect()
      .then(() => {
        return streamingTtsProxy.synthesize(text, {
          voice: opts.voice || config?.tts?.defaultVoice,
          speedRatio: opts.speedRatio || 1.0,
          volumeRatio: opts.volumeRatio || 1.0,
          pitchRatio: opts.pitchRatio || 1.0
        });
      })
      .catch(reject);
  });
}

/**
 * 直接连接火山引擎的流式TTS播放
 */
async function playWithStreamingTtsDirect(text, opts = {}) {
  try {
    const result = await streamingTtsService.synthesizeAndPlay(text, {
      voiceType: opts.voice || config?.tts?.defaultVoice,
      encoding: opts.encoding || 'mp3',
      speedRatio: opts.speedRatio || 1.0,
      volumeRatio: opts.volumeRatio || 1.0,
      pitchRatio: opts.pitchRatio || 1.0,
      emotion: opts.emotion,
      language: opts.language,
      textType: opts.textType || 'plain',
      silenceDuration: opts.silenceDuration || 125
    });
    
    console.log('[TTS] 流式播放完成:', result);
    emitTts({ type: 'streaming_complete', text, result });
    
    return result;
    
  } catch (error) {
    console.warn('[TTS] 流式播放失败，降级到传统TTS:', error);
    emitTts({ type: 'streaming_fallback', text, error: error.message });
    
    // 降级到传统TTS
    return ttsPlay(text, opts);
  }
}
export function clearTtsCache(){ for(const u of cache.values()) URL.revokeObjectURL(u); cache.clear(); }

// 可选：导出底层请求，便于调试
export const _rawTtsRequest = requestTTS;
